/*
 * Copyright 2020-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sagacity.framework.util;

import com.sagacity.framework.api.model.response.ResponseEntity;
import com.sagacity.framework.exception.AppException;
import lombok.extern.slf4j.Slf4j;

import java.util.function.BiFunction;
import java.util.function.Function;

import static com.sagacity.framework.api.constant.ResponseCode.HYSTRIX;

/**
 * Rpc调用客户端
 * @author xingyun
 * @date 2021-12-08 8:53
 */
@Slf4j
public class RpcClient {


    /**
     * <p>
     *     调用RPC单个参数接口方法
     *     示例：
     *          ResponseEntity<Integer> resp = RpcClient.call(OrderService::insert,order)
     * </p>
     * @param function 调用的接口函数
     * @param t 参数
     * @param <T> 参数泛型
     * @param <R> 结果泛型
     * @return 结果
     */
    public static <T,R> R call(Function<T, ResponseEntity<R>> function, T t, Function<T, R> hystrixFunction){
        // 调用实际接口
        ResponseEntity<R> resp = function.apply(t);
        // 处理整个结果为空情况（备注：降级结果没有设置，可设置为null）
        if(resp == null){
            return hystrixFunction.apply(t);
        }
        // 调用成功
        if(resp.isOk()){
            return resp.getData();
        }else{
            throw new AppException(resp.getCode(),resp.getMsg());
        }
    }

    /**
     * <p>
     *     调用RPC单个参数接口方法
     *     示例：
     *          ResponseEntity<Integer> resp = RpcClient.call(OrderService::insert,order)
     * </p>
     * @param function 调用的接口函数
     * @param t 参数
     * @param <T> 参数泛型
     * @param <R> 结果泛型
     * @return 结果
     */
    public static <T,R> R call(Function<T, ResponseEntity<R>> function, T t, String hystrixMsg){
        // 调用实际接口
        ResponseEntity<R> resp = function.apply(t);
        // 处理整个结果为空情况（备注：降级结果没有设置，可设置为null）
        if(resp == null){
            exceptionHystrix(hystrixMsg);
        }
        // 调用成功
        if(resp.isOk()){
            return resp.getData();
        }else{
            throw new AppException(resp.getCode(),resp.getMsg());
        }
    }

    private static void exceptionHystrix(String hystrixMsg) {
        if(StringUtil.isBlank(hystrixMsg)){
            hystrixMsg = HYSTRIX.getMsg();
        }
        throw new AppException(HYSTRIX, hystrixMsg);
    }

    /**
     * <p>
     *     调用RPC单个参数接口方法
     *     示例：
     *          ResponseEntity<Integer> resp = RpcClient.call(OrderService::insert,order,new TypeReference<>(){})
     * </p>
     * @param function 调用的接口函数
     * @param t 参数
     * @param <T> 参数泛型
     * @param <R> 结果泛型
     * @return 结果
     */
    public static <T,R> R call(Function<T, ResponseEntity<R>> function, T t){
        return call(function,t,HYSTRIX.getMsg());
    }

    /**
     * <p>
     *     调用RPC两个参数接口方法
     *     示例：
     *          ResponseEntity<Integer> resp = RpcClient.call(OrderService::insert,order,param2,new TypeReference<>(){})
     * </p>
     * @param function 调用的接口函数
     * @param t 参数
     * @param <T> 参数泛型
     * @param <R> 结果泛型
     * @return 结果
     */
    public static <T,U,R> R call(BiFunction<T,U, ResponseEntity<R>> function, T t,U u){
        return call(function,t,u,HYSTRIX.getMsg());
    }

    /**
     * <p>
     *     调用RPC两个参数接口方法
     *     示例：
     *          ResponseEntity<Integer> resp = RpcClient.call(OrderService::insert,order,param2,new TypeReference<>(){})
     * </p>
     * @param function 调用的接口函数
     * @param t 参数
     * @param hystrixMsg 熔断提示
     * @param <T> 参数泛型
     * @param <R> 结果泛型
     * @return 结果
     */
    public static <T,U,R> R call(BiFunction<T,U, ResponseEntity<R>> function, T t,U u,String hystrixMsg){
        // 调用实际接口
        ResponseEntity<R> resp = function.apply(t,u);
        // 处理整个结果为空情况（备注：降级结果没有设置，可设置为null）
        if(resp == null){
            exceptionHystrix(hystrixMsg);
        }
        if(resp.isOk()){
            return resp.getData();
        }else{
            throw new AppException(resp.getCode(),resp.getMsg());
        }
    }
}
